2.2 HTTP 与 Web
Web 应用的应用层协议是 超文本传输协议(HyperText Transmission Protocol,HTTP)。其建立起了一个客户—服务器体系的网络。在该协议下,客户程序与服务器程序通过交换 HTTP 报文进行会话。
Web 页面(又称文档)是由 对象 组成的,一个对象就是一个文件,例如 HTML 文件、图像文件等。每个 Web 页面由一个 基本的 HTML 文件 与若干的 引用文件 组成。引用文件通过 统一资源定位符(Uniform Resource Locator,URL) 进行引用。一种 URL 的基本格式如下:
<协议名>://<用户名>:<口令>@<主机名>/<路径名>:<端口号>
更多资料详见:MDN 什么是 URL?
HTTP 使用 TCP 作为运输协议,这表明 HTTP 本身不担心数据丢失问题。其拥有一个知名端口号 80。此外,HTTP 是一个 无状态协议,使用 HTTP 协议的服务器不会保存或维护客户的状态信息。
连接方式
HTTP 建立 TCP 连接的方式有两种:HTTP/1.0 使用 非持续连接;HTTP/1.1 及之后的版本使用 持续连接。在非持续连接中,客户与服务器之间的每次请求与响应都需要建立一条 TCP 连接,在响应接受后,TCP 连接立刻关闭,不再响应客户后续发送的请求。在持续连接中,服务器返回 TCP 连接建立的响应后会持续一段时间的连接状态直到超时或客户主动关闭,客户可以在一条 TCP 连接中发送多次 HTTP 请求,甚至在 流水线模式 下可以同时发送多条 HTTP 请求,不必等待发送的某条 HTTP 请求被服务器响应并返回所需数据后才能发送一条新的 HTTP 请求。
为了比较持续连接相比于非持续连接的的优势,我们引入 往返时间(Round Trip Time,RTT) 的定义:一个分组从客户到服务器再返回客户所花费的时间。RTT 显然包括分组的传播时延、在路由器与交换机上的排队时延与处理时延等。
考虑到在非持续连接中,为了从服务器获取一个资源,需要经历 ”三次握手“,即:
- 客户端发起 TCP 连接;
- 服务器响应 TCP 连接请求,连接建立;
- 客户端发出 HTTP 请求;
- 服务器响应 HTTP 请求并返回所需的文件。
可以看到,在这种模式下,为了返回一个文件,需要 两个 RTT 与文件传输用时 的和。
而在持续连接中,在花费一个 RTT 建立 TCP 连接后,之后每次进行文件传输都只需要花费一个 RTT,相比于非持续连接省下了将近一倍的时间。发出的请求越多,相比非持续连接省下的时间就越多。而在流水线模式下,由于所有请求几乎同时发出,有望在一个或几个 RTT 内就能完成所有的请求,省下了更多的时间。因此,HTTP/1.1 默认便采用流水线方式的持续 HTTP 连接。
报文格式
HTTP 报文有两种:请求报文 与 响应报文。其均使用 ASCII 码编写,是可以直接阅读的。
一条 HTTP 请求报文的格式如下:
- 第一行为 请求行(Request Line)。分为三个由空格分隔的字段:方法字段、URL 字段、HTTP 版本字段。
- 后面若干 首部行(Header Line)。包含若干
<首部字段名>:<字段值>的键值对,装载一些服务器能识别的控制与终端版本等信息。 - 首部行后一个空行,表示报文结束。
- (可选)若干 实体行(Entity Body)。当方法字段为
POST时使用,装载用户在表单中输入的值。
POST 方法才能向服务器提交信息。例如 GET 方法也能向服务器提交信息,此时我们提交的信息会被以形如:<键>=<值> 的形式嵌入到 URL 中。常见的方法字段有:
GET向服务器请求信息。POST向服务器提交信息。HEAD只返回某个网页的头部信息,用于建立索引或者开发者调试。PUT向服务器提交对象。DELETE要求服务器删除某个对象。
一条 HTTP 响应报文的格式与请求报文类似,如下:
- 第一行为 状态行(Status Line)。分为三个由空格分隔的字段:HTTP 版本字段、状态码、状态信息。
- 后面若干 首部行(Header Line)。包含若干
<首部字段名>:<字段值>的键值对,装载一些客户端能识别的控制与终端版本等信息。 - 若干 实体行(Entity Body)。响应报文的主要内容,装载了请求的对象数据。
常见的状态码及其状态信息的含义如下,详细请参见:RFC 7231 - HTTP/1.1状态码定义与 MDN HTTP状态码。
200 OK请求成功。301 Moved Permanently请求资源的 URL 已永久更改。在响应中给出了新的 URL。400 Bad Request客户端的请求错误或无效,服务器无法处理请求。403 Forbidden客户端没有访问内容的权限。404 Not Found服务器找不到请求的资源。500 Internal Server Error服务器内部发生问题。
常见的首部信息如下,详细请参见 W3C HTTP/1.1协议与 MDN HTTP Headers。
Host:指定目标服务器的域名和端口(HTTP/1.1要求必须携带)。Date:消息生成的日期和时间(GMT格式)。User-Agent:标识客户端类型(如浏览器、操作系统)。Cookie:发送服务器设置的Cookie信息。Connection:管理连接状态(如keep-alive保持长连接,close关闭连接)。Content-Type:声明资源的MIME类型(如text/html,application/json)。
Cookies
HTTP 使用 cookie 对用户进行跟踪,其包含四个部分:
- 在 HTTP 请求报文中的 cookie 首部行
- 在 HTTP 响应报文中的 cookie 首部行
- 由用户端系统中管理的本地 cookie 文件
- 存储在 Web 服务器后端数据库中的相关信息
借助 cookie,Web 应用便可以在无状态的 HTTP 的基础上实现一个持续的用户会话,然而其本身存在严重的隐私问题。
Web 缓存
Web 缓存器(Web Cache),又称 代理服务器(Proxy Server)。其可以代替原始服务器响应主机的 HTTP 请求。一台主机可以向 Web 缓存器发出 HTTP 请求,由该缓存器代替主机向目标服务器发送 HTTP 请求。当目标服务器返回文件时,Web 缓存器在本地保存一份对象副本。当下次主机再次请求相同的文件时(这被称作 命中),可以直接由 Web 缓存器调用本地存储的副本返回。
在这个环境下,Web 缓存器既是服务器又是客户端。
基于二八定律,主机访问的大量内容可能都是相同的。因此对主机来说,Web 缓存器的存在大大减少了请求的响应时间。对服务器与整个网络来说,通过将大量重复的请求本地化处理,减轻了上述两者的负担与流量开销。
然而,Web 缓存器中存储的内容不总是最新的,因此 HTTP 设计了 条件 GET 机制,通过 if-modified-since 首字段与 304 Not Modified 状态配合以允许缓存器判断本地的副本是否为最新,其原理详见 MDN If-Modified-Since 与 MDN 304 Not Modified。
HTTP/2 与 HTTP/3
HTTP/1.1 的持续连接+流水线工作模式仍然存在问题,其中以 队首阻塞(Head of Line Blocking) 问题尤为突出。其基本思想如下:假设客户端先后向服务器请求了一个很大的对象与若干较小的对象。由于 HTTP/1.1 采用的是“先来先服务”的原则,因此服务器将花费大量时间先将大对象传完,然后再传其余的小对象。尤其是在传输过程中若发生错误,需要重传时,后面等待传输的对象也会保持等待状态,反映在用户上就是整个页面大量内容加载不出来。
HTTP/2 协议引入了一系列新机制。它引入了 成帧机制,将报文分为小帧,在连接上依照优先级交错地发送请求与响应报文。因此,原本需要等待大对象传输的小对象可以直接插在大对象的传输过程中完成传送,大大加快了传输速率。此外,HTTP/2 协议还允许服务器 先于用户请求到来之前 提前向用户推送包含服务器分析出用户可能即将请求发送的对象的相应报文。
HTTP/2 协议仍然在 TCP 或 TLS 上工作,其引入的新机制只是缓解而非彻底解决了 HTTP/1.1 面临的部分问题。例如队首阻塞问题,在发生重传时,后续的帧传输一样会被暂停。尤其是 TLS 的”三次握手“与”慢启动“机制的制约,HTTP/2 的传输速度仍然不够理想。因此,基于 QUIC 的 HTTP/3 协议已经正式发布。关于 QUIC 的介绍,请见上述链接或 3.8 QUIC 与运输层功能的演化。